/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha3

import (
	"errors"
	"fmt"
	"maps"
	"reflect"
	"slices"
	"sort"
	"unsafe"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	apimachineryconversion "k8s.io/apimachinery/pkg/conversion"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/conversion"

	clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1"
	clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
	v1beta1conditions "sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1"
	utilconversion "sigs.k8s.io/cluster-api/util/conversion"
)

var apiVersionGetter = func(_ schema.GroupKind) (string, error) {
	return "", errors.New("apiVersionGetter not set")
}

func SetAPIVersionGetter(f func(gk schema.GroupKind) (string, error)) {
	apiVersionGetter = f
}

func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error {
	dst := dstRaw.(*clusterv1.Cluster)

	if err := Convert_v1alpha3_Cluster_To_v1beta2_Cluster(src, dst, nil); err != nil {
		return err
	}

	if src.Spec.InfrastructureRef != nil {
		infraRef, err := convertToContractVersionedObjectReference(src.Spec.InfrastructureRef)
		if err != nil {
			return err
		}
		dst.Spec.InfrastructureRef = infraRef
	}

	if src.Spec.ControlPlaneRef != nil {
		controlPlaneRef, err := convertToContractVersionedObjectReference(src.Spec.ControlPlaneRef)
		if err != nil {
			return err
		}
		dst.Spec.ControlPlaneRef = controlPlaneRef
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
	dst.Status.Conditions = nil

	// Move legacy conditions (v1alpha3), failureReason and failureMessage to the deprecated field.
	if src.Status.Conditions != nil || src.Status.FailureReason != nil || src.Status.FailureMessage != nil {
		dst.Status.Deprecated = &clusterv1.ClusterDeprecatedStatus{}
		dst.Status.Deprecated.V1Beta1 = &clusterv1.ClusterV1Beta1DeprecatedStatus{}
		if src.Status.Conditions != nil {
			Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&src.Status.Conditions, &dst.Status.Deprecated.V1Beta1.Conditions)
		}
		dst.Status.Deprecated.V1Beta1.FailureReason = src.Status.FailureReason
		dst.Status.Deprecated.V1Beta1.FailureMessage = src.Status.FailureMessage
	}

	// Given this is a bool and there is no timestamp associated with it, when this condition is set, its timestamp
	// will be "now". See https://github.com/kubernetes-sigs/cluster-api/issues/3798#issuecomment-708619826 for more
	// discussion.
	if src.Status.ControlPlaneInitialized {
		v1beta1conditions.MarkTrue(dst, clusterv1.ControlPlaneInitializedV1Beta1Condition)
	}

	// Manually restore data.
	restored := &clusterv1.Cluster{}
	ok, err := utilconversion.UnmarshalData(src, restored)
	if err != nil {
		return err
	}

	// Recover intent for bool values converted to *bool.
	clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused)

	initialization := clusterv1.ClusterInitializationStatus{}
	restoredControlPlaneInitialized := restored.Status.Initialization.ControlPlaneInitialized
	restoredInfrastructureProvisioned := restored.Status.Initialization.InfrastructureProvisioned
	clusterv1.Convert_bool_To_Pointer_bool(src.Status.ControlPlaneReady, ok, restoredControlPlaneInitialized, &initialization.ControlPlaneInitialized)
	clusterv1.Convert_bool_To_Pointer_bool(src.Status.InfrastructureReady, ok, restoredInfrastructureProvisioned, &initialization.InfrastructureProvisioned)
	if !reflect.DeepEqual(initialization, clusterv1.ClusterInitializationStatus{}) {
		dst.Status.Initialization = initialization
	}

	for i, fd := range dst.Status.FailureDomains {
		srcFD, ok := src.Status.FailureDomains[fd.Name]
		if !ok {
			return fmt.Errorf("failure domain %q not found in source data", fd.Name)
		}
		var restoredFDControlPlane *bool
		for _, restoredFD := range restored.Status.FailureDomains {
			if restoredFD.Name == fd.Name {
				restoredFDControlPlane = restoredFD.ControlPlane
				break
			}
		}
		clusterv1.Convert_bool_To_Pointer_bool(srcFD.ControlPlane, ok, restoredFDControlPlane, &fd.ControlPlane)
		dst.Status.FailureDomains[i] = fd
	}

	// Recover other values
	if ok {
		dst.Spec.AvailabilityGates = restored.Spec.AvailabilityGates
		dst.Spec.Topology = restored.Spec.Topology
		dst.Status.Conditions = restored.Status.Conditions
		dst.Status.ControlPlane = restored.Status.ControlPlane
		dst.Status.Workers = restored.Status.Workers
	}

	return nil
}

func (dst *Cluster) ConvertFrom(srcRaw conversion.Hub) error {
	src := srcRaw.(*clusterv1.Cluster)

	if err := Convert_v1beta2_Cluster_To_v1alpha3_Cluster(src, dst, nil); err != nil {
		return err
	}

	if src.Spec.InfrastructureRef.IsDefined() {
		infraRef, err := convertToObjectReference(src.Spec.InfrastructureRef, src.Namespace)
		if err != nil {
			return err
		}
		dst.Spec.InfrastructureRef = infraRef
	}

	if src.Spec.ControlPlaneRef.IsDefined() {
		controlPlaneRef, err := convertToObjectReference(src.Spec.ControlPlaneRef, src.Namespace)
		if err != nil {
			return err
		}
		dst.Spec.ControlPlaneRef = controlPlaneRef
	}

	if dst.Spec.ClusterNetwork != nil && dst.Spec.ClusterNetwork.APIServerPort != nil &&
		*dst.Spec.ClusterNetwork.APIServerPort == 0 {
		dst.Spec.ClusterNetwork.APIServerPort = nil
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1alpha3).
	dst.Status.Conditions = nil

	// Retrieve legacy conditions (v1alpha3), failureReason and failureMessage from the deprecated field.
	if src.Status.Deprecated != nil {
		if src.Status.Deprecated.V1Beta1 != nil {
			if src.Status.Deprecated.V1Beta1.Conditions != nil {
				Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&src.Status.Deprecated.V1Beta1.Conditions, &dst.Status.Conditions)
			}
			dst.Status.FailureReason = src.Status.Deprecated.V1Beta1.FailureReason
			dst.Status.FailureMessage = src.Status.Deprecated.V1Beta1.FailureMessage
		}
	}

	// Set the v1alpha3 boolean status field if the v1alpha4 condition was true
	if v1beta1conditions.IsTrue(src, clusterv1.ControlPlaneInitializedV1Beta1Condition) {
		dst.Status.ControlPlaneInitialized = true
	}

	// Move initialization to old fields
	dst.Status.ControlPlaneReady = ptr.Deref(src.Status.Initialization.ControlPlaneInitialized, false)
	dst.Status.InfrastructureReady = ptr.Deref(src.Status.Initialization.InfrastructureProvisioned, false)

	// Preserve Hub data on down-conversion except for metadata
	if err := utilconversion.MarshalData(src, dst); err != nil {
		return err
	}

	return nil
}

func (src *Machine) ConvertTo(dstRaw conversion.Hub) error {
	dst := dstRaw.(*clusterv1.Machine)

	if err := Convert_v1alpha3_Machine_To_v1beta2_Machine(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToContractVersionedObjectReference(&src.Spec, &dst.Spec); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
	dst.Status.Conditions = nil

	// Move legacy conditions (v1alpha3), failureReason and failureMessage to the deprecated field.
	if src.Status.Conditions != nil || src.Status.FailureReason != nil || src.Status.FailureMessage != nil {
		dst.Status.Deprecated = &clusterv1.MachineDeprecatedStatus{}
		dst.Status.Deprecated.V1Beta1 = &clusterv1.MachineV1Beta1DeprecatedStatus{}
		if src.Status.Conditions != nil {
			Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&src.Status.Conditions, &dst.Status.Deprecated.V1Beta1.Conditions)
		}
		dst.Status.Deprecated.V1Beta1.FailureReason = src.Status.FailureReason
		dst.Status.Deprecated.V1Beta1.FailureMessage = src.Status.FailureMessage
	}

	// Manually restore data.
	restored := &clusterv1.Machine{}
	ok, err := utilconversion.UnmarshalData(src, restored)
	if err != nil {
		return err
	}

	// Recover intent for bool values converted to *bool.
	initialization := clusterv1.MachineInitializationStatus{}
	restoredBootstrapDataSecretCreated := restored.Status.Initialization.BootstrapDataSecretCreated
	restoredInfrastructureProvisioned := restored.Status.Initialization.InfrastructureProvisioned
	clusterv1.Convert_bool_To_Pointer_bool(src.Status.BootstrapReady, ok, restoredBootstrapDataSecretCreated, &initialization.BootstrapDataSecretCreated)
	clusterv1.Convert_bool_To_Pointer_bool(src.Status.InfrastructureReady, ok, restoredInfrastructureProvisioned, &initialization.InfrastructureProvisioned)
	if !reflect.DeepEqual(initialization, clusterv1.MachineInitializationStatus{}) {
		dst.Status.Initialization = initialization
	}

	// Recover other values
	if ok {
		dst.Spec.MinReadySeconds = restored.Spec.MinReadySeconds
		dst.Spec.ReadinessGates = restored.Spec.ReadinessGates
		dst.Spec.Taints = restored.Spec.Taints
		dst.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Deletion.NodeDeletionTimeoutSeconds
		dst.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Deletion.NodeVolumeDetachTimeoutSeconds
		dst.Status.NodeInfo = restored.Status.NodeInfo
		dst.Status.CertificatesExpiryDate = restored.Status.CertificatesExpiryDate
		dst.Status.Deletion = restored.Status.Deletion
		dst.Status.Conditions = restored.Status.Conditions
	}

	return nil
}

func (dst *Machine) ConvertFrom(srcRaw conversion.Hub) error {
	src := srcRaw.(*clusterv1.Machine)

	if err := Convert_v1beta2_Machine_To_v1alpha3_Machine(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToObjectReference(&src.Spec, &dst.Spec, src.Namespace); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1alpha3).
	dst.Status.Conditions = nil

	// Retrieve legacy conditions (v1alpha3), failureReason and failureMessage from the deprecated field.
	if src.Status.Deprecated != nil {
		if src.Status.Deprecated.V1Beta1 != nil {
			if src.Status.Deprecated.V1Beta1.Conditions != nil {
				Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&src.Status.Deprecated.V1Beta1.Conditions, &dst.Status.Conditions)
			}
			dst.Status.FailureReason = src.Status.Deprecated.V1Beta1.FailureReason
			dst.Status.FailureMessage = src.Status.Deprecated.V1Beta1.FailureMessage
		}
	}

	// Move initialization to old fields
	dst.Status.BootstrapReady = ptr.Deref(src.Status.Initialization.BootstrapDataSecretCreated, false)
	dst.Status.InfrastructureReady = ptr.Deref(src.Status.Initialization.InfrastructureProvisioned, false)

	dropEmptyStringsMachineSpec(&dst.Spec)

	// Preserve Hub data on down-conversion except for metadata
	if err := utilconversion.MarshalData(src, dst); err != nil {
		return err
	}

	return nil
}

func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error {
	dst := dstRaw.(*clusterv1.MachineSet)

	if err := Convert_v1alpha3_MachineSet_To_v1beta2_MachineSet(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToContractVersionedObjectReference(&src.Spec.Template.Spec, &dst.Spec.Template.Spec); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
	dst.Status.Conditions = nil

	// Move failureReason, failureMessage and replica counters to the deprecated field.
	dst.Status.Deprecated = &clusterv1.MachineSetDeprecatedStatus{}
	dst.Status.Deprecated.V1Beta1 = &clusterv1.MachineSetV1Beta1DeprecatedStatus{}
	dst.Status.Deprecated.V1Beta1.FailureReason = src.Status.FailureReason
	dst.Status.Deprecated.V1Beta1.FailureMessage = src.Status.FailureMessage
	dst.Status.Deprecated.V1Beta1.ReadyReplicas = src.Status.ReadyReplicas
	dst.Status.Deprecated.V1Beta1.AvailableReplicas = src.Status.AvailableReplicas
	dst.Status.Deprecated.V1Beta1.FullyLabeledReplicas = src.Status.FullyLabeledReplicas

	if src.Spec.MinReadySeconds == 0 {
		dst.Spec.Template.Spec.MinReadySeconds = nil
	} else {
		dst.Spec.Template.Spec.MinReadySeconds = &src.Spec.MinReadySeconds
	}

	// Manually restore data.
	restored := &clusterv1.MachineSet{}
	if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
		return err
	}
	dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates
	dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints
	dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds
	dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds
	if restored.Status.Deprecated != nil && restored.Status.Deprecated.V1Beta1 != nil {
		dst.Status.Deprecated.V1Beta1.Conditions = restored.Status.Deprecated.V1Beta1.Conditions
	}
	dst.Status.Conditions = restored.Status.Conditions
	dst.Status.AvailableReplicas = restored.Status.AvailableReplicas
	dst.Status.ReadyReplicas = restored.Status.ReadyReplicas
	dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas
	dst.Spec.MachineNaming = restored.Spec.MachineNaming
	return nil
}

func (dst *MachineSet) ConvertFrom(srcRaw conversion.Hub) error {
	src := srcRaw.(*clusterv1.MachineSet)

	if err := Convert_v1beta2_MachineSet_To_v1alpha3_MachineSet(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToObjectReference(&src.Spec.Template.Spec, &dst.Spec.Template.Spec, src.Namespace); err != nil {
		return err
	}

	// Reset replica counters from autogenerated conversions
	// NOTE: replica counters with a new semantic should not be automatically be converted into old replica counters.
	dst.Status.AvailableReplicas = 0
	dst.Status.ReadyReplicas = 0

	// Retrieve failureReason, failureMessage and replica counters from the deprecated field.
	if src.Status.Deprecated != nil {
		if src.Status.Deprecated.V1Beta1 != nil {
			dst.Status.FailureReason = src.Status.Deprecated.V1Beta1.FailureReason
			dst.Status.FailureMessage = src.Status.Deprecated.V1Beta1.FailureMessage
			dst.Status.ReadyReplicas = src.Status.Deprecated.V1Beta1.ReadyReplicas
			dst.Status.AvailableReplicas = src.Status.Deprecated.V1Beta1.AvailableReplicas
			dst.Status.FullyLabeledReplicas = src.Status.Deprecated.V1Beta1.FullyLabeledReplicas
		}
	}

	dst.Spec.MinReadySeconds = ptr.Deref(src.Spec.Template.Spec.MinReadySeconds, 0)

	dropEmptyStringsMachineSpec(&dst.Spec.Template.Spec)

	// Preserve Hub data on down-conversion except for metadata
	if err := utilconversion.MarshalData(src, dst); err != nil {
		return err
	}
	return nil
}

func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error {
	dst := dstRaw.(*clusterv1.MachineDeployment)

	if err := Convert_v1alpha3_MachineDeployment_To_v1beta2_MachineDeployment(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToContractVersionedObjectReference(&src.Spec.Template.Spec, &dst.Spec.Template.Spec); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
	dst.Status.Conditions = nil

	// Move replica counters to the deprecated field.
	dst.Status.Deprecated = &clusterv1.MachineDeploymentDeprecatedStatus{}
	dst.Status.Deprecated.V1Beta1 = &clusterv1.MachineDeploymentV1Beta1DeprecatedStatus{}
	dst.Status.Deprecated.V1Beta1.ReadyReplicas = src.Status.ReadyReplicas
	dst.Status.Deprecated.V1Beta1.AvailableReplicas = src.Status.AvailableReplicas
	dst.Status.Deprecated.V1Beta1.UpdatedReplicas = src.Status.UpdatedReplicas
	dst.Status.Deprecated.V1Beta1.UnavailableReplicas = src.Status.UnavailableReplicas

	dst.Spec.Template.Spec.MinReadySeconds = src.Spec.MinReadySeconds

	// Manually restore data.
	restored := &clusterv1.MachineDeployment{}
	ok, err := utilconversion.UnmarshalData(src, restored)
	if err != nil {
		return err
	}

	// Recover intent for bool values converted to *bool.
	clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused)

	// Recover other values
	if ok {
		dst.Spec.Deletion.Order = restored.Spec.Deletion.Order
		dst.Spec.Remediation = restored.Spec.Remediation
		dst.Spec.MachineNaming = restored.Spec.MachineNaming
		dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates
		dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints
		dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds
		dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds
		dst.Spec.Rollout.After = restored.Spec.Rollout.After
		if restored.Status.Deprecated != nil && restored.Status.Deprecated.V1Beta1 != nil {
			dst.Status.Deprecated.V1Beta1.Conditions = restored.Status.Deprecated.V1Beta1.Conditions
		}
		dst.Status.Conditions = restored.Status.Conditions
		dst.Status.AvailableReplicas = restored.Status.AvailableReplicas
		dst.Status.ReadyReplicas = restored.Status.ReadyReplicas
		dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas
	}

	return nil
}

func (dst *MachineDeployment) ConvertFrom(srcRaw conversion.Hub) error {
	src := srcRaw.(*clusterv1.MachineDeployment)

	if err := Convert_v1beta2_MachineDeployment_To_v1alpha3_MachineDeployment(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToObjectReference(&src.Spec.Template.Spec, &dst.Spec.Template.Spec, src.Namespace); err != nil {
		return err
	}

	// Reset replica counters from autogenerated conversions
	// NOTE: replica counters with a new semantic should not be automatically be converted into old replica counters.
	dst.Status.AvailableReplicas = 0
	dst.Status.ReadyReplicas = 0

	// Retrieve failureReason, failureMessage and replica counters from the deprecated field.
	if src.Status.Deprecated != nil {
		if src.Status.Deprecated.V1Beta1 != nil {
			dst.Status.ReadyReplicas = src.Status.Deprecated.V1Beta1.ReadyReplicas
			dst.Status.AvailableReplicas = src.Status.Deprecated.V1Beta1.AvailableReplicas
			dst.Status.UpdatedReplicas = src.Status.Deprecated.V1Beta1.UpdatedReplicas
			dst.Status.UnavailableReplicas = src.Status.Deprecated.V1Beta1.UnavailableReplicas
		}
	}

	dst.Spec.MinReadySeconds = src.Spec.Template.Spec.MinReadySeconds

	dropEmptyStringsMachineSpec(&dst.Spec.Template.Spec)

	// Preserve Hub data on down-conversion except for metadata
	if err := utilconversion.MarshalData(src, dst); err != nil {
		return err
	}

	return nil
}

func (src *MachineHealthCheck) ConvertTo(dstRaw conversion.Hub) error {
	dst := dstRaw.(*clusterv1.MachineHealthCheck)

	if err := Convert_v1alpha3_MachineHealthCheck_To_v1beta2_MachineHealthCheck(src, dst, nil); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
	dst.Status.Conditions = nil

	// Move legacy conditions (v1alpha3) to the deprecated field.
	if src.Status.Conditions != nil {
		dst.Status.Deprecated = &clusterv1.MachineHealthCheckDeprecatedStatus{}
		dst.Status.Deprecated.V1Beta1 = &clusterv1.MachineHealthCheckV1Beta1DeprecatedStatus{}
		Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&src.Status.Conditions, &dst.Status.Deprecated.V1Beta1.Conditions)
	}

	// Manually restore data.
	restored := &clusterv1.MachineHealthCheck{}
	ok, err := utilconversion.UnmarshalData(src, restored)
	if err != nil {
		return err
	}

	dst.Spec.Checks.UnhealthyMachineConditions = restored.Spec.Checks.UnhealthyMachineConditions

	clusterv1.Convert_int32_To_Pointer_int32(src.Status.ExpectedMachines, ok, restored.Status.ExpectedMachines, &dst.Status.ExpectedMachines)
	clusterv1.Convert_int32_To_Pointer_int32(src.Status.CurrentHealthy, ok, restored.Status.CurrentHealthy, &dst.Status.CurrentHealthy)
	clusterv1.Convert_int32_To_Pointer_int32(src.Status.RemediationsAllowed, ok, restored.Status.RemediationsAllowed, &dst.Status.RemediationsAllowed)

	if !ok {
		return nil
	}

	if restored.Spec.Remediation.TriggerIf.UnhealthyInRange != "" {
		dst.Spec.Remediation.TriggerIf.UnhealthyInRange = restored.Spec.Remediation.TriggerIf.UnhealthyInRange
	}
	dst.Status.Conditions = restored.Status.Conditions

	return nil
}

func (dst *MachineHealthCheck) ConvertFrom(srcRaw conversion.Hub) error {
	src := srcRaw.(*clusterv1.MachineHealthCheck)

	if err := Convert_v1beta2_MachineHealthCheck_To_v1alpha3_MachineHealthCheck(src, dst, nil); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1alpha3).
	dst.Status.Conditions = nil

	// Retrieve legacy conditions (v1alpha3) from the deprecated field.
	if src.Status.Deprecated != nil {
		if src.Status.Deprecated.V1Beta1 != nil {
			if src.Status.Deprecated.V1Beta1.Conditions != nil {
				Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&src.Status.Deprecated.V1Beta1.Conditions, &dst.Status.Conditions)
			}
		}
	}

	if dst.Spec.RemediationTemplate != nil {
		dst.Spec.RemediationTemplate.Namespace = src.Namespace
	}

	// Preserve Hub data on down-conversion except for metadata
	if err := utilconversion.MarshalData(src, dst); err != nil {
		return err
	}

	return nil
}

func (src *MachinePool) ConvertTo(dstRaw conversion.Hub) error {
	dst := dstRaw.(*clusterv1.MachinePool)

	if err := Convert_v1alpha3_MachinePool_To_v1beta2_MachinePool(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToContractVersionedObjectReference(&src.Spec.Template.Spec, &dst.Spec.Template.Spec); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1alpha4 conditions should not be automatically be converted into v1beta2 conditions.
	dst.Status.Conditions = nil

	// Move legacy conditions (v1alpha3), failureReason, failureMessage and replica counters to the deprecated field.
	dst.Status.Deprecated = &clusterv1.MachinePoolDeprecatedStatus{}
	dst.Status.Deprecated.V1Beta1 = &clusterv1.MachinePoolV1Beta1DeprecatedStatus{}
	if src.Status.Conditions != nil {
		Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&src.Status.Conditions, &dst.Status.Deprecated.V1Beta1.Conditions)
	}
	dst.Status.Deprecated.V1Beta1.FailureReason = src.Status.FailureReason
	dst.Status.Deprecated.V1Beta1.FailureMessage = src.Status.FailureMessage
	dst.Status.Deprecated.V1Beta1.ReadyReplicas = src.Status.ReadyReplicas
	dst.Status.Deprecated.V1Beta1.AvailableReplicas = src.Status.AvailableReplicas
	dst.Status.Deprecated.V1Beta1.UnavailableReplicas = src.Status.UnavailableReplicas
	dst.Spec.Template.Spec.MinReadySeconds = src.Spec.MinReadySeconds

	// Manually restore data.
	restored := &clusterv1.MachinePool{}
	ok, err := utilconversion.UnmarshalData(src, restored)
	if err != nil {
		return err
	}

	// Recover intent for bool values converted to *bool.
	initialization := clusterv1.MachinePoolInitializationStatus{}
	restoredBootstrapDataSecretCreated := restored.Status.Initialization.BootstrapDataSecretCreated
	restoredInfrastructureProvisioned := restored.Status.Initialization.InfrastructureProvisioned
	clusterv1.Convert_bool_To_Pointer_bool(src.Status.BootstrapReady, ok, restoredBootstrapDataSecretCreated, &initialization.BootstrapDataSecretCreated)
	clusterv1.Convert_bool_To_Pointer_bool(src.Status.InfrastructureReady, ok, restoredInfrastructureProvisioned, &initialization.InfrastructureProvisioned)
	if !reflect.DeepEqual(initialization, clusterv1.MachinePoolInitializationStatus{}) {
		dst.Status.Initialization = initialization
	}

	// Recover other values
	if ok {
		dst.Spec.Template.Spec.ReadinessGates = restored.Spec.Template.Spec.ReadinessGates
		dst.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeDeletionTimeoutSeconds
		dst.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds = restored.Spec.Template.Spec.Deletion.NodeVolumeDetachTimeoutSeconds
		dst.Spec.Template.Spec.Taints = restored.Spec.Template.Spec.Taints
		dst.Status.Conditions = restored.Status.Conditions
		dst.Status.AvailableReplicas = restored.Status.AvailableReplicas
		dst.Status.ReadyReplicas = restored.Status.ReadyReplicas
		dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas
	}

	return nil
}

func (dst *MachinePool) ConvertFrom(srcRaw conversion.Hub) error {
	src := srcRaw.(*clusterv1.MachinePool)

	if err := Convert_v1beta2_MachinePool_To_v1alpha3_MachinePool(src, dst, nil); err != nil {
		return err
	}

	if err := convertMachineSpecToObjectReference(&src.Spec.Template.Spec, &dst.Spec.Template.Spec, src.Namespace); err != nil {
		return err
	}

	// Reset conditions from autogenerated conversions
	// NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1alpha3).
	dst.Status.Conditions = nil

	// Reset replica counters from autogenerated conversions
	// NOTE: replica counters with a new semantic should not be automatically be converted into old replica counters.
	dst.Status.ReadyReplicas = 0
	dst.Status.AvailableReplicas = 0

	// Retrieve legacy conditions (v1alpha3), failureReason, failureMessage and replica counters from the deprecated field.
	if src.Status.Deprecated != nil {
		if src.Status.Deprecated.V1Beta1 != nil {
			if src.Status.Deprecated.V1Beta1.Conditions != nil {
				Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&src.Status.Deprecated.V1Beta1.Conditions, &dst.Status.Conditions)
			}
			dst.Status.FailureReason = src.Status.Deprecated.V1Beta1.FailureReason
			dst.Status.FailureMessage = src.Status.Deprecated.V1Beta1.FailureMessage
			dst.Status.ReadyReplicas = src.Status.Deprecated.V1Beta1.ReadyReplicas
			dst.Status.AvailableReplicas = src.Status.Deprecated.V1Beta1.AvailableReplicas
			dst.Status.UnavailableReplicas = src.Status.Deprecated.V1Beta1.UnavailableReplicas
		}
	}

	// Move initialization to old fields
	dst.Status.BootstrapReady = ptr.Deref(src.Status.Initialization.BootstrapDataSecretCreated, false)
	dst.Status.InfrastructureReady = ptr.Deref(src.Status.Initialization.InfrastructureProvisioned, false)

	dst.Spec.MinReadySeconds = src.Spec.Template.Spec.MinReadySeconds

	dropEmptyStringsMachineSpec(&dst.Spec.Template.Spec)

	return utilconversion.MarshalData(src, dst)
}

func Convert_v1beta2_MachineSetStatus_To_v1alpha3_MachineSetStatus(in *clusterv1.MachineSetStatus, out *MachineSetStatus, _ apimachineryconversion.Scope) error {
	// Status.Conditions was introduced in v1alpha4, thus requiring a custom conversion function; the values is going to be preserved in an annotation thus allowing roundtrip without loosing informations
	// V1Beta2 was added in v1beta1.
	return autoConvert_v1beta2_MachineSetStatus_To_v1alpha3_MachineSetStatus(in, out, nil)
}

func Convert_v1alpha3_ClusterSpec_To_v1beta2_ClusterSpec(in *ClusterSpec, out *clusterv1.ClusterSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_ClusterSpec_To_v1beta2_ClusterSpec(in, out, s); err != nil {
		return err
	}
	if in.ClusterNetwork != nil {
		if err := Convert_v1alpha3_ClusterNetwork_To_v1beta2_ClusterNetwork(in.ClusterNetwork, &out.ClusterNetwork, s); err != nil {
			return err
		}
	}

	return nil
}

func Convert_v1beta2_ClusterSpec_To_v1alpha3_ClusterSpec(in *clusterv1.ClusterSpec, out *ClusterSpec, s apimachineryconversion.Scope) error {
	// NOTE: custom conversion func is required because spec.Topology does not exist in v1alpha3
	// AvailabilityGates was added in v1alpha3
	if err := autoConvert_v1beta2_ClusterSpec_To_v1alpha3_ClusterSpec(in, out, s); err != nil {
		return err
	}
	if !reflect.DeepEqual(in.ClusterNetwork, clusterv1.ClusterNetwork{}) {
		out.ClusterNetwork = &ClusterNetwork{}
		if err := Convert_v1beta2_ClusterNetwork_To_v1alpha3_ClusterNetwork(&in.ClusterNetwork, out.ClusterNetwork, s); err != nil {
			return err
		}
	}

	return nil
}

func Convert_v1alpha3_ClusterNetwork_To_v1beta2_ClusterNetwork(in *ClusterNetwork, out *clusterv1.ClusterNetwork, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_ClusterNetwork_To_v1beta2_ClusterNetwork(in, out, s); err != nil {
		return err
	}
	if in.Services != nil {
		if err := autoConvert_v1alpha3_NetworkRanges_To_v1beta2_NetworkRanges(in.Services, &out.Services, s); err != nil {
			return err
		}
	}
	if in.Pods != nil {
		if err := autoConvert_v1alpha3_NetworkRanges_To_v1beta2_NetworkRanges(in.Pods, &out.Pods, s); err != nil {
			return err
		}
	}

	return nil
}

func Convert_v1beta2_ClusterNetwork_To_v1alpha3_ClusterNetwork(in *clusterv1.ClusterNetwork, out *ClusterNetwork, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1beta2_ClusterNetwork_To_v1alpha3_ClusterNetwork(in, out, s); err != nil {
		return err
	}
	if !reflect.DeepEqual(in.Services, clusterv1.NetworkRanges{}) {
		out.Services = &NetworkRanges{}
		if err := autoConvert_v1beta2_NetworkRanges_To_v1alpha3_NetworkRanges(&in.Services, out.Services, s); err != nil {
			return err
		}
	}

	if !reflect.DeepEqual(in.Pods, clusterv1.NetworkRanges{}) {
		out.Pods = &NetworkRanges{}
		if err := autoConvert_v1beta2_NetworkRanges_To_v1alpha3_NetworkRanges(&in.Pods, out.Pods, s); err != nil {
			return err
		}
	}

	return nil
}

func Convert_v1beta2_ClusterStatus_To_v1alpha3_ClusterStatus(in *clusterv1.ClusterStatus, out *ClusterStatus, s apimachineryconversion.Scope) error {
	// V1Beta2 was added in v1beta1.
	if err := autoConvert_v1beta2_ClusterStatus_To_v1alpha3_ClusterStatus(in, out, s); err != nil {
		return err
	}

	// Move FailureDomains
	if in.FailureDomains != nil {
		out.FailureDomains = FailureDomains{}
		for _, fd := range in.FailureDomains {
			out.FailureDomains[fd.Name] = FailureDomainSpec{
				ControlPlane: ptr.Deref(fd.ControlPlane, false),
				Attributes:   fd.Attributes,
			}
		}
	}

	return nil
}

func Convert_v1alpha3_MachineRollingUpdateDeployment_To_v1beta2_MachineDeploymentRolloutStrategyRollingUpdate(in *MachineRollingUpdateDeployment, out *clusterv1.MachineDeploymentRolloutStrategyRollingUpdate, _ apimachineryconversion.Scope) error {
	out.MaxUnavailable = in.MaxUnavailable
	out.MaxSurge = in.MaxSurge
	return nil
}

func Convert_v1beta2_MachineDeploymentRolloutStrategyRollingUpdate_To_v1alpha3_MachineRollingUpdateDeployment(in *clusterv1.MachineDeploymentRolloutStrategyRollingUpdate, out *MachineRollingUpdateDeployment, _ apimachineryconversion.Scope) error {
	out.MaxUnavailable = in.MaxUnavailable
	out.MaxSurge = in.MaxSurge
	return nil
}

func Convert_v1alpha3_MachineHealthCheckSpec_To_v1beta2_MachineHealthCheckSpec(in *MachineHealthCheckSpec, out *clusterv1.MachineHealthCheckSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_MachineHealthCheckSpec_To_v1beta2_MachineHealthCheckSpec(in, out, s); err != nil {
		return err
	}

	for _, c := range in.UnhealthyConditions {
		out.Checks.UnhealthyNodeConditions = append(out.Checks.UnhealthyNodeConditions, clusterv1.UnhealthyNodeCondition{
			Type:           c.Type,
			Status:         c.Status,
			TimeoutSeconds: clusterv1.ConvertToSeconds(&c.Timeout),
		})
	}
	out.Checks.NodeStartupTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeStartupTimeout)
	out.Remediation.TriggerIf.UnhealthyLessThanOrEqualTo = in.MaxUnhealthy
	if in.RemediationTemplate != nil {
		if err := clusterv1beta1.Convert_v1_ObjectReference_To_v1beta2_MachineHealthCheckRemediationTemplateReference(in.RemediationTemplate, &out.Remediation.TemplateRef, s); err != nil {
			return err
		}
	}

	return nil
}

func Convert_v1beta2_MachineHealthCheckSpec_To_v1alpha3_MachineHealthCheckSpec(in *clusterv1.MachineHealthCheckSpec, out *MachineHealthCheckSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1beta2_MachineHealthCheckSpec_To_v1alpha3_MachineHealthCheckSpec(in, out, s); err != nil {
		return err
	}

	for _, c := range in.Checks.UnhealthyNodeConditions {
		out.UnhealthyConditions = append(out.UnhealthyConditions, UnhealthyCondition{
			Type:    c.Type,
			Status:  c.Status,
			Timeout: ptr.Deref(clusterv1.ConvertFromSeconds(c.TimeoutSeconds), metav1.Duration{}),
		})
	}
	out.NodeStartupTimeout = clusterv1.ConvertFromSeconds(in.Checks.NodeStartupTimeoutSeconds)
	out.MaxUnhealthy = in.Remediation.TriggerIf.UnhealthyLessThanOrEqualTo
	if in.Remediation.TemplateRef.IsDefined() {
		out.RemediationTemplate = &corev1.ObjectReference{}
		if err := clusterv1beta1.Convert_v1beta2_MachineHealthCheckRemediationTemplateReference_To_v1_ObjectReference(&in.Remediation.TemplateRef, out.RemediationTemplate, s); err != nil {
			return err
		}
	}

	return nil
}

func Convert_v1beta2_MachineHealthCheckStatus_To_v1alpha3_MachineHealthCheckStatus(in *clusterv1.MachineHealthCheckStatus, out *MachineHealthCheckStatus, s apimachineryconversion.Scope) error {
	// V1Beta2 was added in v1beta1.
	return autoConvert_v1beta2_MachineHealthCheckStatus_To_v1alpha3_MachineHealthCheckStatus(in, out, s)
}

func Convert_v1alpha3_ClusterStatus_To_v1beta2_ClusterStatus(in *ClusterStatus, out *clusterv1.ClusterStatus, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_ClusterStatus_To_v1beta2_ClusterStatus(in, out, s); err != nil {
		return err
	}

	// Move FailureDomains
	if in.FailureDomains != nil {
		out.FailureDomains = []clusterv1.FailureDomain{}
		domainNames := slices.Collect(maps.Keys(in.FailureDomains))
		sort.Strings(domainNames)
		for _, name := range domainNames {
			fd := in.FailureDomains[name]
			out.FailureDomains = append(out.FailureDomains, clusterv1.FailureDomain{
				Name:         name,
				ControlPlane: nil, // Note: this field will be computed in ConvertTo
				Attributes:   fd.Attributes,
			})
		}
	}

	return nil
}

func Convert_v1alpha3_ObjectMeta_To_v1beta2_ObjectMeta(in *ObjectMeta, out *clusterv1.ObjectMeta, s apimachineryconversion.Scope) error {
	return autoConvert_v1alpha3_ObjectMeta_To_v1beta2_ObjectMeta(in, out, s)
}

func Convert_v1beta2_MachineStatus_To_v1alpha3_MachineStatus(in *clusterv1.MachineStatus, out *MachineStatus, s apimachineryconversion.Scope) error {
	// V1Beta2 was added in v1beta1.
	if err := autoConvert_v1beta2_MachineStatus_To_v1alpha3_MachineStatus(in, out, s); err != nil {
		return err
	}
	if !reflect.DeepEqual(in.LastUpdated, metav1.Time{}) {
		out.LastUpdated = ptr.To(in.LastUpdated)
	}
	if in.NodeRef.IsDefined() {
		out.NodeRef = &corev1.ObjectReference{
			Name:       in.NodeRef.Name,
			APIVersion: corev1.SchemeGroupVersion.String(),
			Kind:       "Node",
		}
	}
	return nil
}

func Convert_v1beta2_MachineSpec_To_v1alpha3_MachineSpec(in *clusterv1.MachineSpec, out *MachineSpec, s apimachineryconversion.Scope) error {
	// spec.nodeDeletionTimeout was added in v1beta1.
	// ReadinessGates was added in v1beta1.
	if err := autoConvert_v1beta2_MachineSpec_To_v1alpha3_MachineSpec(in, out, s); err != nil {
		return err
	}
	out.NodeDrainTimeout = clusterv1.ConvertFromSeconds(in.Deletion.NodeDrainTimeoutSeconds)
	return nil
}

func Convert_v1beta2_MachineDeploymentSpec_To_v1alpha3_MachineDeploymentSpec(in *clusterv1.MachineDeploymentSpec, out *MachineDeploymentSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1beta2_MachineDeploymentSpec_To_v1alpha3_MachineDeploymentSpec(in, out, s); err != nil {
		return err
	}
	if !reflect.DeepEqual(in.Rollout.Strategy, clusterv1.MachineDeploymentRolloutStrategy{}) {
		out.Strategy = &MachineDeploymentStrategy{}
		out.Strategy.Type = MachineDeploymentStrategyType(in.Rollout.Strategy.Type)
		if !reflect.DeepEqual(in.Rollout.Strategy.RollingUpdate, clusterv1.MachineDeploymentRolloutStrategyRollingUpdate{}) {
			out.Strategy.RollingUpdate = &MachineRollingUpdateDeployment{}
			if err := Convert_v1beta2_MachineDeploymentRolloutStrategyRollingUpdate_To_v1alpha3_MachineRollingUpdateDeployment(&in.Rollout.Strategy.RollingUpdate, out.Strategy.RollingUpdate, s); err != nil {
				return err
			}
		}
	}

	return nil
}

func Convert_v1beta2_MachineDeploymentStatus_To_v1alpha3_MachineDeploymentStatus(in *clusterv1.MachineDeploymentStatus, out *MachineDeploymentStatus, s apimachineryconversion.Scope) error {
	// Status.Conditions was introduced in v1alpha4, thus requiring a custom conversion function; the values is going to be preserved in an annotation thus allowing roundtrip without loosing informations
	// V1Beta2 was added in v1beta1.
	return autoConvert_v1beta2_MachineDeploymentStatus_To_v1alpha3_MachineDeploymentStatus(in, out, s)
}

func Convert_v1alpha3_MachineStatus_To_v1beta2_MachineStatus(in *MachineStatus, out *clusterv1.MachineStatus, s apimachineryconversion.Scope) error {
	// Status.version has been removed in v1beta1, thus requiring custom conversion function. the information will be dropped.
	if err := autoConvert_v1alpha3_MachineStatus_To_v1beta2_MachineStatus(in, out, s); err != nil {
		return err
	}
	if in.LastUpdated != nil && !reflect.DeepEqual(in.LastUpdated, &metav1.Time{}) {
		out.LastUpdated = *in.LastUpdated
	}
	if in.NodeRef != nil && !reflect.DeepEqual(in.NodeRef, &corev1.ObjectReference{}) {
		out.NodeRef.Name = in.NodeRef.Name
	}
	return nil
}

func Convert_v1beta2_MachineSetSpec_To_v1alpha3_MachineSetSpec(in *clusterv1.MachineSetSpec, out *MachineSetSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1beta2_MachineSetSpec_To_v1alpha3_MachineSetSpec(in, out, s); err != nil {
		return err
	}

	out.DeletePolicy = string(in.Deletion.Order)
	return nil
}

func Convert_v1alpha3_MachineDeploymentSpec_To_v1beta2_MachineDeploymentSpec(in *MachineDeploymentSpec, out *clusterv1.MachineDeploymentSpec, s apimachineryconversion.Scope) error {
	// NOTE: v1beta2 MachineDeploymentSpec does not have ProgressDeadlineSeconds anymore. But it's fine to just lose this field it was never used.
	if err := autoConvert_v1alpha3_MachineDeploymentSpec_To_v1beta2_MachineDeploymentSpec(in, out, s); err != nil {
		return err
	}
	if in.Strategy != nil {
		out.Rollout.Strategy.Type = clusterv1.MachineDeploymentRolloutStrategyType(in.Strategy.Type)
		if in.Strategy.RollingUpdate != nil {
			if err := Convert_v1alpha3_MachineRollingUpdateDeployment_To_v1beta2_MachineDeploymentRolloutStrategyRollingUpdate(in.Strategy.RollingUpdate, &out.Rollout.Strategy.RollingUpdate, s); err != nil {
				return err
			}
		}
	}

	return nil
}

func Convert_v1alpha3_MachineDeploymentStatus_To_v1beta2_MachineDeploymentStatus(in *MachineDeploymentStatus, out *clusterv1.MachineDeploymentStatus, s apimachineryconversion.Scope) error {
	return autoConvert_v1alpha3_MachineDeploymentStatus_To_v1beta2_MachineDeploymentStatus(in, out, s)
}

func Convert_v1alpha3_MachineSetStatus_To_v1beta2_MachineSetStatus(in *MachineSetStatus, out *clusterv1.MachineSetStatus, s apimachineryconversion.Scope) error {
	return autoConvert_v1alpha3_MachineSetStatus_To_v1beta2_MachineSetStatus(in, out, s)
}

func Convert_v1_Condition_To_v1alpha3_Condition(_ *metav1.Condition, _ *Condition, _ apimachineryconversion.Scope) error {
	// NOTE: v1beta2 conditions should not be automatically converted into legacy (v1alpha3) conditions.
	return nil
}

func Convert_v1alpha3_Condition_To_v1_Condition(_ *Condition, _ *metav1.Condition, _ apimachineryconversion.Scope) error {
	// NOTE: legacy (v1alpha3) conditions should not be automatically converted into v1beta2 conditions.
	return nil
}

func Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(in *clusterv1.Conditions, out *Conditions) {
	*out = make(Conditions, len(*in))
	for i := range *in {
		(*out)[i] = *(*Condition)(unsafe.Pointer(&(*in)[i]))
	}
}

func Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(in *Conditions, out *clusterv1.Conditions) {
	*out = make(clusterv1.Conditions, len(*in))
	for i := range *in {
		(*out)[i] = *(*clusterv1.Condition)(unsafe.Pointer(&(*in)[i]))
	}
}

func Convert_v1alpha3_MachineSetSpec_To_v1beta2_MachineSetSpec(in *MachineSetSpec, out *clusterv1.MachineSetSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_MachineSetSpec_To_v1beta2_MachineSetSpec(in, out, s); err != nil {
		return err
	}

	out.Deletion.Order = clusterv1.MachineSetDeletionOrder(in.DeletePolicy)
	return nil
}

// Convert_v1alpha3_MachinePoolSpec_To_v1beta2_MachinePoolSpec is an autogenerated conversion function.
func Convert_v1alpha3_MachinePoolSpec_To_v1beta2_MachinePoolSpec(in *MachinePoolSpec, out *clusterv1.MachinePoolSpec, s apimachineryconversion.Scope) error {
	return autoConvert_v1alpha3_MachinePoolSpec_To_v1beta2_MachinePoolSpec(in, out, s)
}

func Convert_v1alpha3_MachinePool_To_v1beta2_MachinePool(in *MachinePool, out *clusterv1.MachinePool, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_MachinePool_To_v1beta2_MachinePool(in, out, s); err != nil {
		return err
	}

	// Replace v1alpha3 finalizer to allow old MachinePools to get deleted.
	if controllerutil.ContainsFinalizer(out, MachinePoolFinalizer) {
		controllerutil.RemoveFinalizer(out, MachinePoolFinalizer)
		controllerutil.AddFinalizer(out, clusterv1.MachinePoolFinalizer)
	}

	return nil
}

func Convert_v1beta2_MachinePool_To_v1alpha3_MachinePool(in *clusterv1.MachinePool, out *MachinePool, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1beta2_MachinePool_To_v1alpha3_MachinePool(in, out, s); err != nil {
		return err
	}

	// Replace v1beta1 finalizer to allow old MachinePools to get deleted.
	if controllerutil.ContainsFinalizer(out, clusterv1.MachinePoolFinalizer) {
		controllerutil.RemoveFinalizer(out, clusterv1.MachinePoolFinalizer)
		controllerutil.AddFinalizer(out, MachinePoolFinalizer)
	}

	return nil
}

func Convert_v1beta2_MachinePoolStatus_To_v1alpha3_MachinePoolStatus(in *clusterv1.MachinePoolStatus, out *MachinePoolStatus, s apimachineryconversion.Scope) error {
	return autoConvert_v1beta2_MachinePoolStatus_To_v1alpha3_MachinePoolStatus(in, out, s)
}

func Convert_v1alpha3_MachinePoolStatus_To_v1beta2_MachinePoolStatus(in *MachinePoolStatus, out *clusterv1.MachinePoolStatus, s apimachineryconversion.Scope) error {
	return autoConvert_v1alpha3_MachinePoolStatus_To_v1beta2_MachinePoolStatus(in, out, s)
}

func Convert_v1alpha3_MachineSpec_To_v1beta2_MachineSpec(in *MachineSpec, out *clusterv1.MachineSpec, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_MachineSpec_To_v1beta2_MachineSpec(in, out, s); err != nil {
		return err
	}
	out.Deletion.NodeDrainTimeoutSeconds = clusterv1.ConvertToSeconds(in.NodeDrainTimeout)
	return nil
}

func Convert_v1alpha3_Bootstrap_To_v1beta2_Bootstrap(in *Bootstrap, out *clusterv1.Bootstrap, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1alpha3_Bootstrap_To_v1beta2_Bootstrap(in, out, s); err != nil {
		return err
	}
	if in.ConfigRef != nil {
		if err := clusterv1beta1.Convert_v1_ObjectReference_To_v1beta2_ContractVersionedObjectReference(in.ConfigRef, &out.ConfigRef, s); err != nil {
			return err
		}
	}
	return nil
}

func Convert_v1beta2_Bootstrap_To_v1alpha3_Bootstrap(in *clusterv1.Bootstrap, out *Bootstrap, s apimachineryconversion.Scope) error {
	if err := autoConvert_v1beta2_Bootstrap_To_v1alpha3_Bootstrap(in, out, s); err != nil {
		return err
	}
	if in.ConfigRef.IsDefined() {
		out.ConfigRef = &corev1.ObjectReference{}
		if err := clusterv1beta1.Convert_v1beta2_ContractVersionedObjectReference_To_v1_ObjectReference(&in.ConfigRef, out.ConfigRef, s); err != nil {
			return err
		}
	}
	return nil
}

func convertMachineSpecToContractVersionedObjectReference(src *MachineSpec, dst *clusterv1.MachineSpec) error {
	infraRef, err := convertToContractVersionedObjectReference(&src.InfrastructureRef)
	if err != nil {
		return err
	}
	dst.InfrastructureRef = infraRef

	if src.Bootstrap.ConfigRef != nil {
		bootstrapRef, err := convertToContractVersionedObjectReference(src.Bootstrap.ConfigRef)
		if err != nil {
			return err
		}
		dst.Bootstrap.ConfigRef = bootstrapRef
	}

	return nil
}

func convertMachineSpecToObjectReference(src *clusterv1.MachineSpec, dst *MachineSpec, namespace string) error {
	if src.InfrastructureRef.IsDefined() {
		infraRef, err := convertToObjectReference(src.InfrastructureRef, namespace)
		if err != nil {
			return err
		}
		dst.InfrastructureRef = *infraRef
	}

	if src.Bootstrap.ConfigRef.IsDefined() {
		bootstrapRef, err := convertToObjectReference(src.Bootstrap.ConfigRef, namespace)
		if err != nil {
			return err
		}
		dst.Bootstrap.ConfigRef = bootstrapRef
	}

	return nil
}

func convertToContractVersionedObjectReference(ref *corev1.ObjectReference) (clusterv1.ContractVersionedObjectReference, error) {
	var apiGroup string
	if ref.APIVersion != "" {
		gv, err := schema.ParseGroupVersion(ref.APIVersion)
		if err != nil {
			return clusterv1.ContractVersionedObjectReference{}, fmt.Errorf("failed to convert object: failed to parse apiVersion: %v", err)
		}
		apiGroup = gv.Group
	}
	return clusterv1.ContractVersionedObjectReference{
		APIGroup: apiGroup,
		Kind:     ref.Kind,
		Name:     ref.Name,
	}, nil
}

func convertToObjectReference(ref clusterv1.ContractVersionedObjectReference, namespace string) (*corev1.ObjectReference, error) {
	apiVersion, err := apiVersionGetter(schema.GroupKind{
		Group: ref.APIGroup,
		Kind:  ref.Kind,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to convert object: %v", err)
	}
	return &corev1.ObjectReference{
		APIVersion: apiVersion,
		Kind:       ref.Kind,
		Namespace:  namespace,
		Name:       ref.Name,
	}, nil
}

func dropEmptyStringsMachineSpec(spec *MachineSpec) {
	dropEmptyString(&spec.Version)
	dropEmptyString(&spec.ProviderID)
	dropEmptyString(&spec.FailureDomain)
}

func dropEmptyString(s **string) {
	if *s != nil && **s == "" {
		*s = nil
	}
}
